#include "stdafx.h"
#include "SerialPort.h"
#include <iostream>
#ifdef _WIN32
#include <process.h>

bool SerialPort::s_bExit = false;
const UINT SLEEP_TIME_INTERVAL = 5;

SerialPort::SerialPort(void)
{
	m_hComm = INVALID_HANDLE_VALUE;

	InitializeCriticalSection(&m_csCommunicationSync);
}
SerialPort::~SerialPort()
{
	ClosePort();
	DeleteCriticalSection(&m_csCommunicationSync);
}
bool SerialPort::InitPort(const char *device, UINT baud, char parity, UINT databits, UINT stopsbits, DWORD dwCommEvents)
{
	// Windows下可根据device名转换为端口号实现，略
	return false;
}
bool SerialPort::InitPort(const char *device, const LPDCB &plDCB)
{
	// Windows下未实现
	return false;
}
void SerialPort::ClosePort()
{
	if (m_hComm != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hComm);
		m_hComm = INVALID_HANDLE_VALUE;
	}
}
bool SerialPort::openPort(const char *device)
{
	// Windows下可根据device名转换为端口号实现，略
	return false;
}

UINT SerialPort::GetBytesInCOM()
{
	DWORD dwError = 0;
	COMSTAT comstat;
	memset(&comstat, 0, sizeof(COMSTAT));

	UINT BytesInQue = 0;
	if (ClearCommError(m_hComm, &dwError, &comstat))
	{
		BytesInQue = comstat.cbInQue;
	}
	return BytesInQue;
}
bool SerialPort::ReadChar(UCHAR &cRecved)
{
	BOOL bResult = TRUE;
	DWORD BytesRead = 0;
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		return false;
	}
	EnterCriticalSection(&m_csCommunicationSync);

	bResult = ReadFile(m_hComm, &cRecved, 1, &BytesRead, NULL);
	if ((!bResult))
	{
		DWORD dwError = GetLastError();

		PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
		LeaveCriticalSection(&m_csCommunicationSync);

		return false;
	}

	LeaveCriticalSection(&m_csCommunicationSync);

	return (BytesRead == 1);
}

bool SerialPort::WriteData(unsigned char *pData, unsigned int length)
{
	BOOL bResult = TRUE;
	DWORD BytesToSend = 0;
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		return false;
	}
	EnterCriticalSection(&m_csCommunicationSync);
	bResult = WriteFile(m_hComm, pData, length, &BytesToSend, NULL);
	if (!bResult)
	{
		DWORD dwError = GetLastError();
		PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
		LeaveCriticalSection(&m_csCommunicationSync);
		return false;
	}
	LeaveCriticalSection(&m_csCommunicationSync);

	return true;
}

#else // Linux实现
#include <cstring>
#ifdef __linux__
#include <sys/ioctl.h>
#endif

SerialPort::SerialPort()
{
	m_hComm = -1;
	pthread_mutex_init(&m_mutex, nullptr);
}

SerialPort::~SerialPort()
{
	ClosePort();
	pthread_mutex_destroy(&m_mutex);
}

bool SerialPort::InitPort(const char *device, UINT baud, char parity, UINT databits, UINT stopsbits, DWORD dwCommEvents)
{
	if (!openPort(device))
		return false;
	pthread_mutex_lock(&m_mutex);
	struct termios newtio;
	memset(&newtio, 0, sizeof(newtio));
	cfsetispeed(&newtio, baud);
	cfsetospeed(&newtio, baud);
	newtio.c_cflag |= CLOCAL | CREAD;
	switch (databits)
	{
	case 7:
		newtio.c_cflag |= CS7;
		break;
	case 8:
	default:
		newtio.c_cflag |= CS8;
		break;
	}
	switch (parity)
	{
	case 'O':
	case 'o':
		newtio.c_cflag |= PARENB | PARODD;
		newtio.c_iflag |= (INPCK | ISTRIP);
		break;
	case 'E':
	case 'e':
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;
		newtio.c_iflag |= (INPCK | ISTRIP);
		break;
	case 'N':
	case 'n':
	default:
		newtio.c_cflag &= ~PARENB;
		break;
	}
	switch (stopsbits)
	{
	case 2:
		newtio.c_cflag |= CSTOPB;
		break;
	case 1:
	default:
		newtio.c_cflag &= ~CSTOPB;
		break;
	}
	newtio.c_cc[VTIME] = 1; // 0.1秒超时
	newtio.c_cc[VMIN] = 0;
	tcflush(m_hComm, TCIFLUSH);
	if (tcsetattr(m_hComm, TCSANOW, &newtio) != 0)
	{
		pthread_mutex_unlock(&m_mutex);
		return false;
	}
	pthread_mutex_unlock(&m_mutex);
	return true;
}

bool SerialPort::InitPort(const char *device, const LPDCB &plDCB)
{
	// Linux下未实现
	return false;
}

void SerialPort::ClosePort()
{
	if (m_hComm != -1)
	{
		close(m_hComm);
		m_hComm = -1;
	}
}

bool SerialPort::openPort(const char *device)
{
	pthread_mutex_lock(&m_mutex);
	m_hComm = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
	if (m_hComm == -1)
	{
		pthread_mutex_unlock(&m_mutex);
		return false;
	}
	fcntl(m_hComm, F_SETFL, 0);
	tcgetattr(m_hComm, &m_oldtio);
	pthread_mutex_unlock(&m_mutex);
	return true;
}

UINT SerialPort::GetBytesInCOM()
{
	int bytes = 0;
	if (ioctl(m_hComm, FIONREAD, &bytes) == -1)
		return 0;
	return (UINT)bytes;
}

bool SerialPort::ReadChar(UCHAR &cRecved)
{
	pthread_mutex_lock(&m_mutex);
	int n = read(m_hComm, &cRecved, 1);
	pthread_mutex_unlock(&m_mutex);
	return n == 1;
}

bool SerialPort::WriteData(unsigned char *pData, unsigned int length)
{
	pthread_mutex_lock(&m_mutex);
	int n = write(m_hComm, pData, length);
	pthread_mutex_unlock(&m_mutex);
	return n == (int)length;
}

#endif